package com.timk.goserver.client.ui.screens;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.timk.goserver.client.board.Board;
import com.timk.goserver.client.board.BoardListener;

/**
 * BoardPanel is the UI element that draws the board.
 * @author TKington
 *
 */
public class BoardPanel extends Composite implements BoardListener { // NOPMD - Complexity
	private final Image[][] boardImages;
	private final Image[][] horzCoordImages;
	private final Image[][] vertCoordImages;
	private final Image [] blankImages;
	
	List listeners = new ArrayList(); // NOPMD - Explicit scoping
	private static int imageSize = 21;
	
	/**
	 * Creates a BoardPanel
	 */
	public BoardPanel() {
		boardImages = new Image[19][19];
		for(int i = 0; i < boardImages.length; i++) {
			for(int j = 0; j < boardImages.length; j++) {
				Image image = new Image();
				boardImages[i][j] = image;
				image.addClickListener(new BoardClickListener(i, j));
			}
		}
		
		VerticalPanel vertPanel = new VerticalPanel();
		vertPanel.setStyleName("goserver-BoardImage");
		
		HorizontalPanel topCoordPanel = new HorizontalPanel();
		HorizontalPanel bottomCoordPanel = new HorizontalPanel();
		
		blankImages = new Image[4];
		
		Image blankImage = new Image();
		topCoordPanel.add(blankImage);
		blankImages[0] = blankImage;
		
		blankImage = new Image();
		bottomCoordPanel.add(blankImage);
		blankImages[1] = blankImage;
		
		horzCoordImages = new Image[19][2];
		vertCoordImages = new Image[2][19];
		
		for(int i = 0; i < 19; i++) {
			Image image = new Image();
			topCoordPanel.add(image);
			horzCoordImages[i][0] = image;
			
			image = new Image();
			bottomCoordPanel.add(image);
			horzCoordImages[i][1] = image;
		}

		blankImage = new Image();
		topCoordPanel.add(blankImage);
		blankImages[2] = blankImage;
		
		blankImage = new Image();
		bottomCoordPanel.add(blankImage);
		blankImages[3] = blankImage;
		
		vertPanel.add(topCoordPanel);
		
		for(int y = 0; y < boardImages.length; y++) {
			HorizontalPanel horzPanel = new HorizontalPanel();
			vertPanel.add(horzPanel);
			
			Image image = new Image();
			horzPanel.add(image);
			vertCoordImages[0][y] = image;
			
			for(int x = 0; x < boardImages.length; x++) {
				horzPanel.add(boardImages[x][y]);
			}
			
			image = new Image();
			horzPanel.add(image);
			vertCoordImages[1][y] = image;
		}
		
		vertPanel.add(bottomCoordPanel);
		
		initWidget(vertPanel);
	}
	
	/**
	 * Shows the given board on the panel.
	 * @param board the board
	 */
	private void showBoard(Board board) { // NOPMD - Complexity
		String baseURL = GWT.getModuleBaseURL();
		
		int boardSize = board.getSize();
		for(int i = 0; i < boardImages.length; i++) {
			for(int j = 0; j < boardImages.length; j++) {
				Image image = boardImages[i][j];
				
				if(i >= boardSize || j >= boardSize) {
					image.setVisible(false);
					continue;
				}
				
				String newUrl = getImageURL(board, i, j);
				
				if(!image.getUrl().equals(baseURL + newUrl)) {
					image.setUrl(newUrl);
				}
				if(!image.isVisible()) {
					image.setVisible(true);
				}
			}
		}
		
		//	Show letter coordinates
		int i = 0;
		for(char c = 'a'; c <= 't'; c++, i++) {
			if(c == 'i') {
				i--;
				continue;
			}

			Image image = horzCoordImages[i][0];
			Image image2 = horzCoordImages[i][1];
			
			if(i >= boardSize) {
				image.setVisible(false);
				image2.setVisible(false);
				continue;
			}
			
			String coordURL = "images/" + imageSize + "/coord_" + c + ".gif";
			
			if(!image.getUrl().equals(baseURL + coordURL)) {
				image.setUrl(coordURL);
				image2.setUrl(coordURL);
			}
			
			if(!image.isVisible()) {
				image.setVisible(true);
				image2.setVisible(true);
			}
		}
		
		//	Show number coordinates
		for(i = 0; i < 19; i++) {
			Image image = vertCoordImages[0][i];
			Image image2 = vertCoordImages[1][i];

			if(i >= boardSize) {
				image.setVisible(false);
				image2.setVisible(false);
				continue;
			}
			
			String coordURL = "images/" + imageSize + "/coord_" + (boardSize - i) + ".gif";
			
			if(!image.getUrl().equals(baseURL + coordURL)) {
				image.setUrl(coordURL);
				image2.setUrl(coordURL);
			}
			
			if(!image.isVisible()) {
				image.setVisible(true);
				image2.setVisible(true);
			}
		}
		
		//	Blank images for corners
		String blankURL = "images/" + imageSize + "/coord_blank.gif";
		if(!blankImages[0].getUrl().equals(baseURL + blankURL)) {
			for(i = 0; i < 4; i++) {
				blankImages[i].setUrl(blankURL);
			}
		}
	}
	
	/**
	 * Returns the relative URL to the image for a given location on the board
	 * @param board the Board
	 * @param x
	 * @param y
	 * @return the relative URL
	 */
	public static String getImageURL(Board board, int x, int y) {
		StringBuffer url = new StringBuffer(40);
		url.append("images/").append(imageSize).append('/');
		
		switch(board.getAt(x, y)) {
		case 1:
			url.append('b');
			break;
		case -1:
			url.append('w');
			break;
		default:
			addImagePrefix(url, x, y, board.getSize());
			break;
		}
		
		switch(board.getMarkAt(x, y)) {
		case Board.MARK_NONE:
			url.append(".gif");
			break;
		case Board.MARK_CURMOVE:
			url.append("_curmove.gif");
			break;
		case Board.MARK_TERRBLACK:
			url.append("_bterr.gif");
			break;
		case Board.MARK_TERRWHITE:
			url.append("_wterr.gif");
			break;
		case Board.MARK_TERRUNKNOWN:
			url.append("_uterr.gif");
			break;
		default:
			url.append("_unknownmark.gif");
			break;
		}
		
		return url.toString();
	}
	
	private static void addImagePrefix(StringBuffer buf, int i, int j, // NOPMD - Complexity
			int boardSize) {
		StringBuffer prefix = new StringBuffer();
		if(j == 0) {
			prefix.append('u');
		} else if(j == boardSize - 1) {
			prefix.append('d');
		} else {
			prefix.append('e');
		}
		
		if(i == 0) {
			prefix.append('l');
		} else if(i == boardSize - 1) {
			prefix.append('r');
		}
		
		if(isHoshi(boardSize, i, j)) {
			buf.append('h');
		} else {
			buf.append(prefix);
		}
	}

	private static boolean isHoshi(int boardSize, int x, int y) {	// NOPMD - Complexity
		if(boardSize == 9) {
			if((x == 2 || x == 6) && (y == 2 || y == 6)) {
				return true;
			}
		}
		else if(boardSize > 9 &&
				(x == 3 || x == boardSize / 2 || x == boardSize - 4) &&
				(y == 3 || y == boardSize / 2 || y == boardSize - 4)) {
			return true;
		}
		return false;
	}
	
	public void boardChanged(Board board) {
		showBoard(board);	
	}
	
	/**
	 * Returns the character for the alt tag of the board image at x, y
	 * @param board the Board
	 * @param x
	 * @param y
	 * @return the character
	 */
	public static char getAltChar(Board board, int x, int y) {
		switch(board.getMarkAt(x, y)) {
		case Board.MARK_NONE:
		case Board.MARK_TERRBLACK:
		case Board.MARK_TERRWHITE:
		case Board.MARK_TERRUNKNOWN:
			break;
		case Board.MARK_CURMOVE:
			if(board.getAt(x, y) == 1) {
				return '#';
			}
			return '@';
		default:
			return '?';
		}
		
		switch(board.getAt(x, y)) {
		case 1:
			return 'X';
		case -1:
			return 'O';
		default:
			if(isHoshi(board.getSize(), x, y)) {
				return ',';
			}
			return '.';
		}
	}
	
	/**
	 * Sets the image size
	 * @param size the size
	 */
	public static void setImageSize(int size) {
		imageSize = size;
	}

	/**
	 * Adds a BoardPanelListener
	 * @param listener the listener
	 */
	public void addListener(BoardPanelListener listener) { listeners.add(listener); }
	
	class BoardClickListener implements ClickListener {
		private final int x;
		private final int y;
		
		/**
		 * Creates a BoardClickListener
		 * @param x the x location
		 * @param y the y location
		 */
		public BoardClickListener(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public void onClick(Widget sender) {
			for (Iterator iter = listeners.iterator(); iter.hasNext();) {
				BoardPanelListener listener = (BoardPanelListener) iter.next();
				listener.onClick(x, y);
			}
		}
	}
}
